package com.yb0os1;

import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.locks.LockSupport;

public class ScheduleService {
    private ExecutorService pool = Executors.newFixedThreadPool(10);
    Trigger trigger = new Trigger();

    public void schedule(Runnable schedule, long delay) {
        Job job = new Job(schedule, System.currentTimeMillis()+delay,delay);
        trigger.jobQueue.offer(job);
        trigger.wakeUp();
    }

    public void schedule(Job job) {
        job.setStartTime(System.currentTimeMillis()+job.getDelay());
        trigger.jobQueue.offer(job);
        trigger.wakeUp();
    }

    //中断任务
    public void interrupt(Job job){
        trigger.jobQueue.removeIf(e->e.equals(job));
    }



    class Trigger {
        private final PriorityBlockingQueue<Job> jobQueue = new PriorityBlockingQueue<>();

        Thread thread = new Thread(() -> {
            while (true) {
                //如果为空 那么阻塞  为了防止虚假唤醒 需要while
                while (jobQueue.isEmpty()) {
                    LockSupport.park();
                }
                //先获取最先要执行的
                Job job = jobQueue.peek();
                if (job.getStartTime()<System.currentTimeMillis()){
                    //任务可以执行
                    //这里一次peek一次poll为了防止执行延迟为1s的任务的时候 插入了延迟为500ms的任务 需要先执行延迟为500ms的任务
                    job = jobQueue.poll();
                    pool.execute(job.getTask());
                    //我们是定时任务 执行了这次还要计算下次要执行的时间点
                    Job nextJob = new Job(job.getTask(),System.currentTimeMillis()+job.getDelay(),job.getDelay());
                    jobQueue.offer(nextJob);
                }else{
                    //最近要执行的任务都不可以执行 延迟等待到开始时间
                    //如果等待的时候添加了一个任务 那么会被唤醒 重新走一遍逻辑
                    LockSupport.parkUntil(job.getStartTime());
                }
            }
        });
        {
            thread.start();
        }
        public void wakeUp() {
            LockSupport.unpark(thread);
        }
    }

    static class Job implements Comparable<Job>{
        private Runnable task;
        private long startTime;
        private long delay;

        public void setStartTime(long startTime) {
            this.startTime = startTime;
        }

        public Job(Runnable task, long delay){
            this.task = task;
            this.delay = delay;
        }
        public Job(Runnable task, long startTime, long delay) {
            this.task = task;
            this.startTime = startTime;
            this.delay = delay;
        }

        public long getStartTime() {
            return startTime;
        }

        public Runnable getTask() {
            return task;
        }

        public long getDelay() {
            return delay;
        }

        @Override
        public int compareTo(Job o) {
            return Long.compare(this.startTime,o.getStartTime());
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Job job = (Job) o;
            return delay == job.delay && Objects.equals(task, job.task);
        }

        @Override
        public int hashCode() {
            return Objects.hash(task, delay);
        }
    }

}